#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "SymbolTable.hpp"
#include "analex.h"


/********************************************************************
*              Implementacao da Classe BasicSymbol                  *
********************************************************************/
BasicSymbol :: BasicSymbol(unsigned short int idx, BasicSymbol *Next){
    this->lexemeIdx = idx;
    this->next = Next;
}

unsigned short int BasicSymbol :: getLexemeIdx(){
    return this->lexemeIdx;
}

BasicSymbol* BasicSymbol :: getNext(){
    return this->next;
}

BasicSymbol :: ~BasicSymbol(){
    if(this->getNext() != NULL) delete next;
}


/********************************************************************
*                Implementacao da Classe IDSymbol                   *
********************************************************************/
IDSymbol :: IDSymbol(unsigned short int idx, IDSymbol *Next, unsigned int symbolType) : BasicSymbol(idx,Next){
    this->symbolType = symbolType;
}

/********************************************************************
*                  Implementacao da Classe Type                     *
********************************************************************/
Type :: Type(char *Class, unsigned short int primitiveType, int arrayLimit){
    this->Class = Class;
    this->primitiveType = primitiveType;
    this->arrayLimit = arrayLimit;
}

Type :: ~Type(){
    if(this->Class) free(this->Class);
}

/********************************************************************
*              Implementacao da Classe VariableSymbol               *
********************************************************************/
VariableSymbol :: VariableSymbol(unsigned short int idx, IDSymbol *Next, Type *type, unsigned int symbolType, unsigned short int Scope, bool active, VariableSymbol* nextVariable) : IDSymbol(idx,Next,symbolType){
    this->type = type;
    this->scope = Scope;
    this->active = active;
    this->nextVariable = nextVariable;
    this->defined = false;
}

VariableSymbol :: ~VariableSymbol(){
    if(this->type) delete this->type;
}

/********************************************************************
*              Implementacao da Classe VariableSymbolList           *
********************************************************************/
VariableSymbolList :: VariableSymbolList(VariableSymbol* thisVar, VariableSymbolList* nextVar){
    this->nextVar = nextVar;
    this->thisVar = thisVar;
}

VariableSymbolList :: ~VariableSymbolList(){
    if(this->nextVar) delete this->nextVar;
}

/********************************************************************
*              Implementacao da Classe AttributeSymbol              *
********************************************************************/
AttributeSymbol :: AttributeSymbol(unsigned short int idx, IDSymbol *Next, unsigned int symbolType, VariableSymbol *Var, AttributeSymbol *NextAttribute) : IDSymbol(idx,Next,symbolType){
    this->attribute = Var;
    this->nextAttribute = NextAttribute;
    this->owner = NULL;
}

AttributeSymbol :: ~AttributeSymbol(){
    if(this->attribute) delete this->attribute;
}

/********************************************************************
*              Implementacao da Classe ParameterSymbol              *
********************************************************************/
ParameterSymbol :: ParameterSymbol(unsigned short int idx, IDSymbol *Next, unsigned int symbolType, VariableSymbol *Var, ParameterSymbol *NextParam) : IDSymbol(idx,Next,symbolType){
    this->param = Var;
    this->nextParam = NextParam;
}

ParameterSymbol :: ~ParameterSymbol(){
    if(this->param) delete this->param;
}

/********************************************************************
*              Implementacao da Classe MethodSymbol                 *
********************************************************************/
MethodSymbol :: MethodSymbol(unsigned short int idx, IDSymbol *Next, unsigned int symbolType, ParameterSymbol *FirstParam, VariableSymbol* FirstVariable, Type *type, MethodSymbol *NextMethod) : IDSymbol(idx,Next,symbolType){
    this->firstParam = FirstParam;
    this->firstVariable = FirstVariable;
    this->returnType = type;
    this->nextMethod = NextMethod;
}

MethodSymbol :: ~MethodSymbol(){
    if(this->returnType) delete this->returnType;
}

/********************************************************************
*              Implementacao da Classe ClassSymbol                  *
********************************************************************/
ClassSymbol :: ClassSymbol(unsigned short int idx, IDSymbol *Next, unsigned int symbolType, ClassSymbol *Extended, MethodSymbol *FirstMethod, AttributeSymbol *FirstAttribute) : IDSymbol(idx,Next,symbolType){
    this->extendedClass = Extended;
    this->firstMethod = FirstMethod;
    this->firstAttribute = FirstAttribute;
}

ClassSymbol :: ~ClassSymbol(){}

/********************************************************************
*             Implementacao da Classe LiteralSymbol                 *
********************************************************************/
LiteralSymbol :: LiteralSymbol(unsigned short int idx, LiteralSymbol *Next) : BasicSymbol(idx,Next){
}


/********************************************************************
*            Implementacao da Classe ReservedWordSymbol             *
********************************************************************/
ReservedWordSymbol :: ReservedWordSymbol(unsigned short int idx, ReservedWordSymbol *Next, unsigned short int Token) : BasicSymbol(idx, Next){
    this->token = Token;
}

unsigned short int ReservedWordSymbol :: getToken(){
    return this->token;
}


/********************************************************************
*            Implementacao da Classe BasicSymbolTable               *
********************************************************************/
BasicSymbolTable :: BasicSymbolTable(){
    this->lexemes = (char*) malloc(STD_LEXEME_LENGTH * sizeof(char));
    this->lexemesLength = STD_LEXEME_LENGTH;
    this->position = 0;
}


int BasicSymbolTable :: hash(char *c){
    char *p;
    unsigned h = 0, g;
    for (p = c; *p != '\0'; p = p + 1) {
        h = (h << 4) + (*p);
        if ((g = h & 0xf0000000)) {
            h = h ^ (g >> 4);
            h = h ^g;
        }
    }
    return h % PRIME_NUMBER;
}

void BasicSymbolTable :: addSymbol(char* lex){
    unsigned int newLexemeLen = strlen(lex);

    if (this->lexemesLength < this->position + newLexemeLen + 1) {
        unsigned int lexemesLen = this->lexemesLength;
        lexemesLen += (newLexemeLen > STD_LEXEME_LENGTH) ? newLexemeLen + STD_LEXEME_LENGTH : STD_LEXEME_LENGTH;
        this->lexemesLength = lexemesLen;
        this->lexemes = (char*) realloc(this->lexemes, this->lexemesLength * sizeof(char));
    }

    strcpy(this->lexemes + this->position, lex);

}


/********************************************************************
*            Implementacao da Classe IDSymbolTable                  *
********************************************************************/
IDSymbolTable :: IDSymbolTable() : BasicSymbolTable(){
    for(int i=0; i<PRIME_NUMBER; i++) this->table[i] = NULL;
}

IDSymbolTable :: ~IDSymbolTable(){
    for(int i=0; i<PRIME_NUMBER; i++) delete this->table[i];
}

/*
* Procura por um ID de um tipo, classe ou variavel
* Caso nao tenha o mesmo lexema, ou mesmo tipo, procuro pelo proximo simbolo,
* o laco while nao termina ate achar o simbolo desejado, retornando um ponteiro para ele,
* ou encontrar o fim da lista encadeada, retornando null.
*/
IDSymbol* IDSymbolTable :: searchID(char *lex, unsigned int symbolType){
    unsigned int h = this->hash(lex);
    IDSymbol *aux = (IDSymbol*) this->table[h];

    while(aux != NULL){
        if (symbolType == aux->symbolType){
            char *Lexeme;
            Lexeme = this->lexemes + aux->getLexemeIdx();
            if(strcmp(Lexeme,lex) == 0) return aux;
        }
        aux = (IDSymbol*) aux->getNext();
    }
    return NULL;
}

void IDSymbolTable :: addSymbol(IDSymbol *symbol, char* lex){
    this->BasicSymbolTable::addSymbol(lex);

    unsigned int newLexemeLen = strlen(lex);
    unsigned int h = this->hash(lex);
    symbol->lexemeIdx = this->position;
    if(!this->table[h]) this->table[h] = symbol;
    else {
        IDSymbol *aux;
        aux = this->table[h];
        this->table[h] = symbol;
        symbol->next = aux;
    }

    this->position += newLexemeLen + 1;

    return;
}


void IDSymbolTable :: printTable(){
    IDSymbol *id;
    ClassSymbol *Class;
    AttributeSymbol *Attribute;
    MethodSymbol *Method;
    ParameterSymbol *Parameter;
    VariableSymbol *Variable;
    Type *type;
    fprintf(stdout,"\n%s\n","---------------------------------------------------------------");
    fprintf(stdout,"%s\n","                    ID SYMBOL TABLE                             ");
    fprintf(stdout,"%s","---------------------------------------------------------------");
    for(int i=0; i<PRIME_NUMBER; i++){
        id = this->table[i];
        while(id!=NULL){
            if(id->symbolType == CLASS_SYMBOL){
                Class = (ClassSymbol*)id;
                fprintf(stdout, "\n  Class: %s\n", this->lexemes + Class->getLexemeIdx());
                if(Class->extendedClass) fprintf(stdout, "  Extends: %s\n", this->lexemes + Class->extendedClass->getLexemeIdx());
                if(Class->firstAttribute){
                    fprintf(stdout, "  Attributes:\n");
                    Attribute = Class->firstAttribute;
                    while(Attribute){
                        Variable = Attribute->attribute;
                        type = Variable->type;
                        fprintf(stdout, "   Attribute\n    Name: %s\n", this->lexemes + Attribute->getLexemeIdx());
                        if(type->Class) fprintf(stdout, "    Type: %s\n", type->Class);
                        else{
                            if(type->arrayLimit == -1) {
                                fprintf(stdout, "    Type: ");
                                printToken(type->primitiveType);
                                fprintf(stdout, "\n");
                            }
                            else{
                                fprintf(stdout, "    Type: ");
                                printToken(type->primitiveType);
                                fprintf(stdout, "[%d]\n", type->arrayLimit);
                            }
                        }
                        Attribute = Attribute->nextAttribute;
                    }
                }
                if(Class->firstMethod){
                    fprintf(stdout, "  Methods:\n");
                    Method = Class->firstMethod;
                    while(Method){
                        type = Method->returnType;
                        fprintf(stdout, "   Method\n    Name: %s\n", this->lexemes + Method->getLexemeIdx());
                        if(type->Class) fprintf(stdout, "    Return Type: %s\n", type->Class);
                        else{
                            if(type->primitiveType == VOID) fprintf(stdout, "    Return Type: STATIC VOID\n");
                            else{
                                if(type->arrayLimit == -1) {
                                    fprintf(stdout, "    Return Type: ");
                                    printToken(type->primitiveType);
                                    fprintf(stdout, "\n");
                                }
                                else{
                                    fprintf(stdout, "    Return Type: ");
                                    printToken(type->primitiveType);
                                    fprintf(stdout, "[%d]\n", type->arrayLimit);
                                }
                            }
                        }
                        Parameter = Method->firstParam;
                        while(Parameter){
                            type = Parameter->param->type;
                            fprintf(stdout, "    Parameter\n     Name: %s\n", this->lexemes + Parameter->getLexemeIdx());
                            if(type->Class) fprintf(stdout, "     Type: %s\n", type->Class);
                            else{
                                if(type->arrayLimit == -1) {
                                    fprintf(stdout, "     Type: ");
                                    printToken(type->primitiveType);
                                    fprintf(stdout, "\n");
                                }
                                else{
                                    fprintf(stdout, "     Type: ");
                                    printToken(type->primitiveType);
                                    fprintf(stdout, "[%d]\n", type->arrayLimit);
                                }
                            }
                            Parameter = Parameter->nextParam;
                        }
                        Variable = Method->firstVariable;
                        while(Variable){
                            type = Variable->type;
                            fprintf(stdout, "    Variable\n     Name: %s\n", this->lexemes + Variable->getLexemeIdx());
                            if(type->Class) fprintf(stdout, "     Type: %s\n", type->Class);
                            else{
                                if(type->arrayLimit == -1) {
                                    fprintf(stdout, "     Type: ");
                                    printToken(type->primitiveType);
                                    fprintf(stdout, "\n");
                                }
                                else{
                                    fprintf(stdout, "     Type: ");
                                    printToken(type->primitiveType);
                                    fprintf(stdout, "[%d]\n", type->arrayLimit);
                                }
                            }
                            Variable = Variable->nextVariable;
                        }
                        Method = Method->nextMethod;
                    }
                }
                fprintf(stdout, "\n");
            }
            id = (IDSymbol*) id->getNext();
        }
    }
}


/********************************************************************
*            Implementacao da Classe LiteralSymbolTable             *
********************************************************************/
LiteralSymbolTable :: LiteralSymbolTable() : BasicSymbolTable(){
    for(int i=0; i<PRIME_NUMBER; i++) this->table[i] = NULL;
}

LiteralSymbolTable :: ~LiteralSymbolTable(){
    for(int i=0; i<PRIME_NUMBER; i++) delete this->table[i];
}

void LiteralSymbolTable :: addSymbol(char* lex){
    this->BasicSymbolTable::addSymbol(lex);

    unsigned int newLexemeLen = strlen(lex);
    unsigned int h = this->hash(lex);
    LiteralSymbol *newSymb = new LiteralSymbol(this->position, NULL);
    if(!this->table[h]) this->table[h] = newSymb;
    else {
        LiteralSymbol *aux;
        aux = this->table[h];
        this->table[h] = newSymb;
        newSymb->next = aux;
    }

    this->position += newLexemeLen + 1;

    return;
}

void LiteralSymbolTable :: printTable(){
    LiteralSymbol *lit;
    fprintf(stdout,"\n%s\n","---------------------------------------------------------------");
    fprintf(stdout,"%s\n","LITERAL TABLE");
    fprintf(stdout,"%s\n","---------------------------------------------------------------");
    fprintf(stdout,"%s\n","Lexeme\t                Hash           Index");
    fprintf(stdout,"%s\n","---------------------------------------------------------------");
    for(int i=0; i<PRIME_NUMBER; i++){
        lit = this->table[i];
        while(lit!=NULL){
            fprintf(stdout,"%-15s\t %10d\t %9d\t \n",this->lexemes + lit->getLexemeIdx(), i, lit->getLexemeIdx());
            lit = (LiteralSymbol*) lit->getNext();
        }
    }
}


/********************************************************************
*            Implementacao da Classe ReservedWordSymbolTable        *
********************************************************************/
void ReservedWordSymbolTable :: addSymbol(char* lex, unsigned short int token){
    this->BasicSymbolTable::addSymbol(lex);

    unsigned int newLexemeLen = strlen(lex);
    unsigned int h = this->hash(lex);
    ReservedWordSymbol *newSymb = new ReservedWordSymbol(this->position, NULL, token);
    if(!this->table[h]) this->table[h] = newSymb;
    else {
        ReservedWordSymbol *aux;
        aux = this->table[h];
        this->table[h] = newSymb;
        newSymb->next = aux;
    }

    this->position += newLexemeLen + 1;

    return;
}

ReservedWordSymbolTable :: ReservedWordSymbolTable() : BasicSymbolTable(){
    for(int i=0; i<PRIME_NUMBER; i++) this->table[i] = NULL;
    this->addSymbol((char*) "class", CLASS);
    this->addSymbol((char*) "extends", EXTENDS);
    this->addSymbol((char*) "public", PUBLIC);
    this->addSymbol((char*) "static", STATIC);
    this->addSymbol((char*) "void", VOID);
    this->addSymbol((char*) "int", INT);
    this->addSymbol((char*) "float", FLOAT);
    this->addSymbol((char*) "boolean", BOOLEAN);
    this->addSymbol((char*) "if", IF);
    this->addSymbol((char*) "else", ELSE);
    this->addSymbol((char*) "while", WHILE);
    this->addSymbol((char*) "System", SYSTEM);
    this->addSymbol((char*) "out", OUT);
    this->addSymbol((char*) "println", PRINTLN);
    this->addSymbol((char*) "in", IN);
    this->addSymbol((char*) "read", READ);
    this->addSymbol((char*) "return", RETURN);
    this->addSymbol((char*) "new", NEW);
    this->addSymbol((char*) "length", LENGTH);
    this->addSymbol((char*) "true", TRUE);
    this->addSymbol((char*) "false", FALSE);
    this->addSymbol((char*) "this", THIS);
    this->addSymbol((char*) "null", JNULL);
}

ReservedWordSymbolTable :: ~ReservedWordSymbolTable(){
    for(int i=0; i<PRIME_NUMBER; i++) delete this->table[i];
}

unsigned int ReservedWordSymbolTable :: searchReservedWord(char *lex){
    unsigned int h = this->hash(lex);
    ReservedWordSymbol *aux = (ReservedWordSymbol*) this->table[h];
    while(aux != NULL){
        char *Lexeme;
        Lexeme = this->lexemes + aux->getLexemeIdx();
        if(strcmp(Lexeme,lex) == 0) return aux->getToken();
        aux = (ReservedWordSymbol*) aux->getNext();
    }
    return false;
}

void ReservedWordSymbolTable :: printTable(){
    ReservedWordSymbol *reservedWord;
    fprintf(stdout,"\n%s\n","---------------------------------------------------------------");
    fprintf(stdout,"%s\n","RESERVED WORD TABLE");
    fprintf(stdout,"%s\n","---------------------------------------------------------------");
    fprintf(stdout,"%s\n","Lexeme\t                Token\t         Hash           Index");
    fprintf(stdout,"%s\n","---------------------------------------------------------------");
    for(int i=0; i<PRIME_NUMBER; i++){
        reservedWord = this->table[i];
        while(reservedWord!=NULL){
            fprintf(stdout,"%-15s %10d %17d %16d \n",this->lexemes + reservedWord->getLexemeIdx(), reservedWord->getToken(), i, reservedWord->getLexemeIdx());
            reservedWord = (ReservedWordSymbol*)reservedWord->getNext();
        }
    }
}
